package com.liuqi.loggers.core.appender;

import com.liuqi.loggers.core.AbstractAppender;
import com.liuqi.loggers.core.Encoder;
import com.liuqi.loggers.core.LoggerEvent;
import com.liuqi.loggers.core.LoggingException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 输出流日志追究器
 *
 * @author liuqi
 * @date 2024/12/12 16:57
 **/
public abstract class OutputStreamAppender extends AbstractAppender {

    protected static byte[] LINE_SEPARATOR = System.getProperty("line.separator", "\n").getBytes();

    protected final ReentrantLock lock = new ReentrantLock();

    protected Encoder encoder;

    protected OutputStream outputStream;

    /**
     * 是否在写完之后, 立即刷新到输出流中
     */
    protected boolean immediateFlush = true;

    @Override
    protected void doAppend(LoggerEvent event) throws LoggingException {
        byte[] encode = encoder.encode(event);
        lock.lock();
        try {
            outputStream.write(encode);
            outputStream.write(LINE_SEPARATOR);
            if (immediateFlush) {
                flush();
            }
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void start() {
        if (encoder == null) {
            throw new NullPointerException("encoder cannot be null");
        }
        encoder.start();
        super.start();
    }

    @Override
    public void stop() {
        if (encoder != null) {
            encoder.stop();
        }
        try {
            flush();
        } catch (IOException e) { }

        try {
            close();
        } catch (IOException e) { }

        super.stop();
    }

    /**
     * 将缓冲区刷新
     *
     * @throws IOException
     */
    protected void flush() throws IOException {
        if (outputStream != null) {
            outputStream.flush();
        }
    }

    /**
     * 关闭此输出流
     *
     * @throws IOException
     */
    protected void close() throws IOException {
        if (outputStream != null) {
            outputStream.close();
        }
    }

    public Encoder getEncoder() {
        return encoder;
    }

    public void setEncoder(Encoder encoder) {
        this.encoder = encoder;
    }

    public OutputStream getOutputStream() {
        return outputStream;
    }

    public void setOutputStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public boolean isImmediateFlush() {
        return immediateFlush;
    }

    public void setImmediateFlush(boolean immediateFlush) {
        this.immediateFlush = immediateFlush;
    }
}
